<template>
  <el-card shadow="hover" class="mt-20">
    <!-- 流程2.给form表单元素添加ref属性绑定ref数据，添加model收集表单数据，添加rules绑定验证规则 -->
    <el-form
      label-width="100px"
      ref="spuFormRef"
      :model="spuFormData"
      :rules="spuFormRules"
    >
      <!-- 流程3.给每个el-form-item 添加prop属性绑定对应的规则 -->
      <!-- 流程4.收集每个表单项数据v-model -->
      <el-form-item label="SPU名称" prop="spuName">
        <el-input
          placeholder="请输入SPU名称"
          v-model="spuFormData.spuName"
        ></el-input>
      </el-form-item>
      <el-form-item label="品牌" prop="tmId">
        <el-select placeholder="请选择" v-model="spuFormData.tmId">
          <el-option
            :label="tm.tmName"
            :value="tm.id"
            v-for="tm in allTrademarkList"
            :key="tm.id"
          ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="SPU描述" prop="description">
        <el-input
          placeholder="请输入SPU描述"
          type="textarea"
          rows="3"
          v-model="spuFormData.description"
        ></el-input>
      </el-form-item>
      <el-form-item label="SPU图片" prop="spuImageList">
        <el-upload
          v-model:file-list="spuFormData.spuImageList"
          :action="`${BASE_URL}/admin/product/fileUpload`"
          list-type="picture-card"
          :on-preview="handlePictureCardPreview"
          :on-remove="handleRemove"
          :limit="MAX_LIMIT"
          :before-upload="beforeAvatarUpload"
          accept="image/jpeg,image/png"
          :on-success="handleAvatarSuccess"
        >
          <el-icon><Plus /></el-icon>
          <template #tip>
            <div class="el-upload__tip">只能上传jpg/png文件，且不超过200kb</div>
          </template>
        </el-upload>
        <el-dialog v-model="dialogVisible" width="30%">
          <img
            w-full
            :src="dialogImageUrl"
            alt="预览图片"
            class="spuDialog__img"
          />
        </el-dialog>
      </el-form-item>
      <el-form-item label="销售属性" prop="spuSaleAttrList">
        <!-- 4.v-model收集数据 -->
        <el-select
          :placeholder="baseSaleAttrPlaceholder"
          class="mr-10"
          v-model="selectedBaseSaleAttr"
        >
          <!-- 2. 列表数据展示的应该是过滤后数据-->
          <!-- 6.:value="`${baseSaleAttr.id}:${baseSaleAttr.name}`" 为了收集到数据然后添加销售属性对象-->
          <el-option
            :label="baseSaleAttr.name"
            :value="`${baseSaleAttr.id}:${baseSaleAttr.name}`"
            v-for="baseSaleAttr in filteredBaseSaleAttrList"
            :key="baseSaleAttr.id"
          ></el-option>
        </el-select>
        <!-- 
          5.给添加按钮添加禁用效果，没选就禁用，有选不禁用 
            给按钮添加点击事件 
          -->
        <el-button
          type="primary"
          :icon="Plus"
          :disabled="!selectedBaseSaleAttr"
          @click="addSaleAttr"
          >添加销售属性</el-button
        >
        <el-table :data="spuFormData.spuSaleAttrList" border class="mt-20">
          <el-table-column
            label="属性"
            type="index"
            width="80"
            align="center"
          ></el-table-column>
          <el-table-column
            label="属性名"
            prop="saleAttrName"
            width="150"
          ></el-table-column>
          <el-table-column label="属性值列表">
            <template v-slot="{ row }">
              <el-tag
                type="success"
                v-for="(spuSaleAttrValue, index) in row.spuSaleAttrValueList"
                :key="spuSaleAttrValue.saleAttrValueName"
                class="mr-10"
                closable
                @close="delSpuSaleAttrValue(row.spuSaleAttrValueList, index)"
              >
                {{ spuSaleAttrValue.saleAttrValueName }}
              </el-tag>
              <el-input
                size="small"
                class="spu__input-w150"
                placeholder="名称"
                v-if="row.isShowEdit"
                ref="inputRef"
                @blur="switchShow(row)"
                @keyup.enter="switchShow(row)"
                v-model="saleAttrValueName"
              />
              <el-button
                :icon="Plus"
                v-else
                @click="switchEdit(row)"
              ></el-button>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150">
            <template v-slot="{ row, $index }">
              <el-popconfirm
                :title="`您确定要删除 ${row.saleAttrName} 销售属性吗？`"
                @confirm="delSaleAttr($index)"
              >
                <template #reference>
                  <el-button
                    type="danger"
                    size="small"
                    :icon="Delete"
                    title="删除"
                  />
                </template>
              </el-popconfirm>
            </template>
          </el-table-column>
        </el-table>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submit">保存</el-button>
        <el-button @click="cancel">关闭</el-button>
      </el-form-item>
    </el-form>
  </el-card>
</template>

<script lang="ts">
export default {
  name: "AddOrUpdateSpu",
};
</script>

<script lang="ts" setup>
import { Plus, Delete } from "@element-plus/icons-vue";
import { ref, reactive, computed, onMounted, nextTick } from "vue";
import type { UploadProps } from "element-plus";
import { ElMessage } from "element-plus";
import { getAllTrademarkListApi } from "@/api/product/trademark";
import { GetAllTrademarkList } from "@/api/product/model/trademarkModel";
import type {
  GetBaseSaleAttrList,
  SpuFormData,
  SpuSaleAttrItem,
  SpuSaleAttrValueList,
  SpuSaleAttrList,
} from "@/api/product/model/spuModel";
import {
  getBaseSaleAttrListApi,
  addSpuApi,
  getSpuImageListApi,
  getSpuSaleAttrListApi,
  updateSpuApi,
} from "@/api/product/spu";
import { useCategoryStore } from "@/stores/category";
import { useSpuStore } from "@/stores/spu";
const categoryStore = useCategoryStore();
const spuStore = useSpuStore();
// 提交保存
const submit = async () => {
  // 整个表单校验
  await spuFormRef.value.validate();

  const id = spuStore.spu.id;
  const data = {
    category3Id: categoryStore.category3Id,
    ...spuFormData,
    // 组件表单收集的到图片数据是 name，response.data
    // 而发送请求需要的图片数据是 imgName imgUrl
    spuImageList: spuFormData.spuImageList.map((img) => {
      return {
        // 断言得到data
        imgUrl: (img.response as { data: string }).data,
        imgName: img.name,
      };
    }),
  };
  if (id) {
    // 修改
    await updateSpuApi({ ...data, id });
  } else {
    // 添加
    await addSpuApi(data);
  }
  ElMessage.success(`${id ? "更新" : "添加"}SPU成功`);
  cancel();
};

// 获取品牌列表和销售属性列表
const baseSaleAttrList = ref<GetBaseSaleAttrList>([]);
const allTrademarkList = ref<GetAllTrademarkList>([]);
onMounted(async () => {
  baseSaleAttrList.value = await getBaseSaleAttrListApi();
});
onMounted(async () => {
  allTrademarkList.value = await getAllTrademarkListApi();
});
// 获取上传图片列表
onMounted(async () => {
  // 只有更新需要请求，添加没有数据不需要请求
  const id = spuStore.spu.id as number;
  if (!id) return;
  // 组件el-upload需要的图片类型是UploadUserFile[]类型
  // 而我们发送请求添加SPU或从服务器获取SpuImageList的类型，都是{ imgName, imgUrl }类型
  const spuImageList = await getSpuImageListApi(id);
  // 将请求回来的{ imgName, imgUrl }类型变成组件需要的UploadUserFile[]类型
  spuFormData.spuImageList = spuImageList.map((img) => {
    return {
      name: img.imgName,
      url: img.imgUrl, // 图片地址（不填就渲染不了图片）
      response: {
        data: img.imgUrl,
      },
    };
  });
});
// 获取销售属性列表
onMounted(async () => {
  // 只有更新需要请求，添加没有数据不需要请求
  const id = spuStore.spu.id as number;
  if (!id) return;
  spuFormData.spuSaleAttrList = await getSpuSaleAttrListApi(id);
});

// 选项式API
// onMounted(async () => {
//   const res = await Promise.all([
//     getBaseSaleAttrListApi(),
//     getAllTrademarkListApi(),
//   ]);
//   baseSaleAttrList.value = res[0];
//   allTrademarkList.value = res[1];
// });
// 两个请求不需要互相等待
// 1.写两个onMounted
// 2.promise.all 都成功才成功
// 3.Promise.allSettled([]) 无论里面的成功还是失败都会成功
// onMounted(async () => {
//    baseSaleAttrList.value = await getBaseSaleAttrListApi();
//   allTrademarkList.value = await getAllTrademarkListApi();
// });

// 销售属性功能
// 1.对baseSaleAttrList进行过滤，判断基础销售属性在不在spuSaleAttrList中，在，过滤；不在，保留
// const filteredBaseSaleAttrList = computed(() => {
//   return baseSaleAttrList.value.filter((item) => {
//     if (
//       spuFormData.spuSaleAttrList.some(
//         (spuSaleAttr) => spuSaleAttr.baseSaleAttrId === item.id
//       )
//     ) {
//       return false;
//     }
//     return true;
//   });
// });
// 简写
const filteredBaseSaleAttrList = computed(() =>
  baseSaleAttrList.value.filter(
    (item) =>
      !spuFormData.spuSaleAttrList.some(
        (spuSaleAttr) => spuSaleAttr.baseSaleAttrId === item.id
      )
  )
);
// 3.定义响应式数据用于收集被选择的显示属性
const selectedBaseSaleAttr = ref("");
// 6.添加销售属性
const addSaleAttr = () => {
  const [id, name] = selectedBaseSaleAttr.value.split(":");
  spuFormData.spuSaleAttrList.push({
    baseSaleAttrId: +id, // 基础销售属性id
    saleAttrName: name, // 基础销售属性的name
    spuSaleAttrValueList: [], // 销售属性值列表
    isShowEdit: false, //  编辑和显示切换的标识，默认显示显示模式
  });
  // 清空选中的值
  selectedBaseSaleAttr.value = "";
};
// 7.删除销售属性
const delSaleAttr = (index: number) => {
  spuFormData.spuSaleAttrList.splice(index, 1);
};
// 计算Placeholder文字
const baseSaleAttrPlaceholder = computed(() => {
  const length = filteredBaseSaleAttrList.value.length;
  return length ? `还剩个${length}销售属性` : `没有了`;
});
/*
  1.给每一行数据添加isShowEdit属性，用于切换编辑模式和显示模式
  2.进入编辑模式，输入框聚焦
  3.回车或者失去焦点，进入显示模式
  4.定义响应式数据，收集输入内容
  5.输入完后切换为显示模式，并把收集到的内容显示
  6.如果输入内容重复，不进行添加 后还是编辑模式
  7.如果输入内容为空，不添加后显示模式
  8.场景，当上一个输入重复内容，然后对新的属性添加属性值，那么就要完成上一个编辑
    定义一个响应式数据isEditing标识正在编辑状态，只有一个input处于编辑状态

*/
const inputRef = ref();
// 收集输入的内容
const saleAttrValueName = ref("");
// 标识input在编辑状态，只有一个input，需要编辑完才能进入下一个input
const isEditing = ref(false);
// 切换为编辑模式
const switchEdit = async (row: SpuSaleAttrItem) => {
  if (isEditing.value) {
    ElMessage.warning("请完成之前的编辑");
    return;
  }
  row.isShowEdit = true;
  isEditing.value = true;
  await nextTick();
  inputRef.value.focus();
};
// 切换为显示模式
const switchShow = (row: SpuSaleAttrItem) => {
  // 如果输入内容重复，不进行添加 后还是编辑模式
  if (
    row.spuSaleAttrValueList.some(
      (spuSaleAttrValue) =>
        spuSaleAttrValue.saleAttrValueName === saleAttrValueName.value
    )
  ) {
    ElMessage.warning("不同添加相同的属性值~");
    return;
  }
  row.isShowEdit = false;
  isEditing.value = false;
  // 如果输入内容为空，不添加后显示模式
  if (!saleAttrValueName.value) return;

  // 添加属性值
  row.spuSaleAttrValueList.push({
    baseSaleAttrId: row.baseSaleAttrId,
    saleAttrValueName: saleAttrValueName.value,
  });
  // 清空输入内容
  saleAttrValueName.value = "";
};

// 删除属性值
const delSpuSaleAttrValue = (
  spuSaleAttrValueList: SpuSaleAttrValueList,
  index: number
) => {
  spuSaleAttrValueList.splice(index, 1);
};

// 上传之前限制图片的类型和大小
const beforeAvatarUpload: UploadProps["beforeUpload"] = (rawFile) => {
  const filesType = ["image/jpeg", "image/png"];
  if (!filesType.includes(rawFile.type)) {
    ElMessage.error("上传的图片只能是jpg或png格式!");
    return false;
  } else if (rawFile.size / 1024 > 200) {
    // 单位字节byte
    ElMessage.error("图片大小不能超过200kb!");
    return false;
  }
  return true;
};
// 上传成功删除提示
const handleAvatarSuccess = () => {
  spuFormRef.value.clearValidate();
};
// 设置上传图片的长度和超过长度隐藏掉上传的+
const MAX_LIMIT = 3;
const isDialogHidden = computed(() => {
  return spuFormData.spuImageList.length < MAX_LIMIT ? "inline-flex" : "none";
});

// 上传图片功能
const BASE_URL = import.meta.env.VITE_API_URL;
// 预览图片
const dialogImageUrl = ref("");
const dialogVisible = ref(false);
// 文件列表移除文件时的钩子
const handleRemove: UploadProps["onRemove"] = (uploadFile, uploadFiles) => {
  console.log(uploadFile, uploadFiles);
};
// 点击文件列表中已上传的文件时的钩子
const handlePictureCardPreview: UploadProps["onPreview"] = (uploadFile) => {
  dialogImageUrl.value = uploadFile.url!;
  dialogVisible.value = true;
};
// 校验表单功能
// 流程1.定义响应式数据（三个）
const spuFormRef = ref();
const spuFormData = reactive<SpuFormData>({
  // 在修改spu时需要兄弟通信pinia，所以改为在spuStore获取数据
  // 看接口
  spuName: spuStore.spu.spuName, // spu名称
  tmId: spuStore.spu.tmId, // 品牌
  description: spuStore.spu.description, // spu描述
  // SPU图片
  spuImageList: [
    // {
    //   "id": 0,
    //   "imgName": "string",
    //   "imgUrl": "string",
    //   "spuId": 0
    // }
  ],
  // SPU销售属性
  spuSaleAttrList: [
    // {
    //   baseSaleAttrId: 0,
    //   id: 0,
    //   saleAttrName: "string",
    //   spuId: 0,
    //   spuSaleAttrValueList: [
    //     {
    //       baseSaleAttrId: 0,
    //       id: 0,
    //       isChecked: "string",
    //       saleAttrName: "string",
    //       saleAttrValueName: "string",
    //       spuId: 0,
    //     },
    //   ],
    // },
  ],
});
const spuSaleAttrListValidator = (
  rule: any,
  value: SpuSaleAttrList,
  callback: any
) => {
  /*
    rule 校验规则信息，包含规则名称等内容
    value 校验的数据
    callback 一个函数（必须要调用）
      callback() 表单校验通过
      callback(new Error('错误信息')) 表单校验失败，参数就是失败提示的错误信息
  */
  //  1.销售属性必须有值
  if (!value.length) {
    callback(new Error("请至少添加一个销售属性"));
    return;
  }
  // 2.每一个销售属性的销售属性值列表至少添加一个销售属性值
  if (value.some((spuSaleAttr) => !spuSaleAttr.spuSaleAttrValueList.length)) {
    callback(new Error("每一个销售属性至少添加一个销售属性值"));
    return;
  } else {
    callback();
  }
};
const spuFormRules = reactive({
  spuName: [{ required: true, message: "请输入spu名称", trigger: "blur" }],
  tmId: [{ required: true, message: "请选择品牌", trigger: "change" }],
  description: [{ required: true, message: "请输入SPU描述", trigger: "blur" }],
  spuImageList: [{ required: true, message: "请上传SPU图片 " }],
  spuSaleAttrList: [
    // 数组需要自定义校验规则，判断数组里面是否有值
    {
      required: true, // 显示*号，同时也是必填项
      validator: spuSaleAttrListValidator, // validator属性:函数,函数里面对数据进行校验
    },
  ],
});
// 修改组件切换标识
const emit = defineEmits(["setIsShow"]);
const cancel = () => {
  emit("setIsShow", 1);
};
</script>

<style scoped>
.spuDialog__img {
  width: 100%;
}
:deep(.el-upload) {
  display: v-bind(isDialogHidden);
}
.spu__input-w150 {
  width: 150px;
}
</style>
